package com.maozzi.service.acl.biz;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.maozzi.api.acl.AclService;
import com.maozzi.api.acl.model.AclUserDTO;
import com.maozzi.api.acl.model.PermissionDTO;
import com.maozzi.api.acl.model.RoleDTO;
import com.maozzi.api.user.UserService;
import com.maozzi.api.user.model.UserDTO;
import com.maozzi.common.enums.BizCodeEnum;
import com.maozzi.common.exception.BizException;
import com.maozzi.common.model.PageDTO;
import com.maozzi.common.model.PageParam;
import com.maozzi.common.util.CloneUtil;
import com.maozzi.common.util.ParamCheckUtil;
import com.maozzi.service.acl.domain.PermissionDomainService;
import com.maozzi.service.acl.domain.RoleDomainService;
import com.maozzi.service.acl.domain.RolePermissionDomainService;
import com.maozzi.service.acl.domain.UserRoleDomainService;
import com.maozzi.service.acl.repository.model.Permission;
import com.maozzi.service.acl.repository.model.Role;
import com.maozzi.service.acl.repository.model.RolePermission;
import com.maozzi.service.acl.repository.model.UserRole;
import javax.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboService;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 权限 - 角色
 *
 * @author maozi
 */
@Slf4j
@DubboService
public class AclServiceImpl implements AclService {

    @Resource
    private UserService userService;
    @Resource
    private UserRoleDomainService userRoleDomainService;
    @Resource
    private RoleDomainService roleDomainService;
    @Resource
    private RolePermissionDomainService rolePermissionDomainService;
    @Resource
    private PermissionDomainService permissionDomainService;
    @Resource
    private RedissonClient redissonClient;

    @Override
    public AclUserDTO getAclUser(Long userId) throws BizException {
        UserDTO userDTO = userService.getUser(userId);

        AclUserDTO aclUserDTO = CloneUtil.to(userDTO, AclUserDTO.class);
        aclUserDTO.setRoles(getUserRole(userId));
        aclUserDTO.setPermissions(getUserPermission(userId));

        return aclUserDTO;
    }

    @Override
    public List<RoleDTO> getUserRole(Long userId) throws BizException {
        List<UserRole> userRoleList = userRoleDomainService.getUserRole(userId);

        return CloneUtil.toList(userRoleList, RoleDTO.class);
    }

    @Override
    public List<PermissionDTO> getUserPermission(Long userId) throws BizException {
        List<PermissionDTO> permissionList = new ArrayList<>();

        List<UserRole> userRoleList = userRoleDomainService.getUserRole(userId);
        List<Long> roleIds = userRoleList.stream().map(UserRole::getRoleId).distinct().collect(Collectors.toList());
        if (ObjectUtils.isEmpty(roleIds)) {
            return permissionList;
        }

        List<RolePermission> rolePermissionList = rolePermissionDomainService.getRolePermissions(roleIds);
        List<Long> permissionIds = rolePermissionList.stream().map(RolePermission::getPermissionId).collect(Collectors.toList());
        if (ObjectUtils.isEmpty(permissionIds)) {
            return permissionList;
        }
        List<Permission> permissions = permissionDomainService.getPermissions(permissionIds);

        return CloneUtil.toList(permissions, PermissionDTO.class);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean putUserRole(AclUserDTO aclUserDTO) throws BizException {
        Long userId = aclUserDTO.getId();
        Long[] roleIds = aclUserDTO.getRoles().stream().map(RoleDTO::getId).toArray(Long[]::new);

        Boolean isPut = userRoleDomainService.putUserRoleBatch(userId, roleIds);
        if (!isPut) {
            throw new BizException(BizCodeEnum.FAILED_OPERATION, "保存用户角色失败");
        }

        return Boolean.TRUE;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean editUserRole(Long userId, AclUserDTO aclUserDTO) throws BizException {
        aclUserDTO.setId(userId);

        // 获取分布锁
        RLock lock = redissonClient.getLock("unfair_lock");
        if (ObjectUtils.isEmpty(lock)) {
            log.error("editUserRole 获取锁失败，锁对象为空");

            return false;
        }
        boolean b;
        try {
            b = lock.tryLock();
            if (b) {
                this.delUserRole(userId);

                this.putUserRole(aclUserDTO);
            }
        } catch (Exception e) {
            log.error("editUserRole 获取锁错误，", e);
        } finally {
            if (lock.isLocked() && lock.isHeldByCurrentThread())
                lock.unlock();
        }

        return true;
    }

    @Override
    public void delUserRole(Long userId) throws BizException {
        Boolean isDel = userRoleDomainService.delUserRoleBatch(userId);
        if (!isDel) {
            throw new BizException(BizCodeEnum.FAILED_OPERATION, "删除用户角色失败");
        }
    }

    @Override
    public RoleDTO getRole(Long roleId) {
        Role role = roleDomainService.getRole(roleId);

        return CloneUtil.to(role, RoleDTO.class);
    }

    @Override
    public RoleDTO putRole(RoleDTO roleDTO) throws BizException {
        ParamCheckUtil.notNull(roleDTO, "参数错误");
        ParamCheckUtil.notBlank(roleDTO.getName(), "角色名称为空");

        Role role = CloneUtil.to(roleDTO, Role.class);

        role = roleDomainService.putRole(role);
        if (ObjectUtils.isEmpty(role)) {
            throw new BizException(BizCodeEnum.FAILED_OPERATION, "角色保存失败");
        }

        return CloneUtil.to(role, RoleDTO.class);
    }

    @Override
    public Boolean editRole(Long roleId, RoleDTO roleDTO) throws BizException {
        Role role = CloneUtil.to(roleDTO, Role.class);

        Boolean isEdit = roleDomainService.editRole(roleId, role);
        if (!isEdit) {
            throw new BizException(BizCodeEnum.FAILED_OPERATION, "角色编辑失败");
        }

        return Boolean.TRUE;
    }

    @Override
    public Boolean delRole(Long roleId) throws BizException {
        Role role = new Role();
        role.setId(roleId);
        role.setDeleted(Boolean.TRUE);

        Boolean isEdit = roleDomainService.editRole(roleId, role);
        if (!isEdit) {
            throw new BizException(BizCodeEnum.FAILED_OPERATION, "角色编辑失败");
        }

        return Boolean.TRUE;
    }

    @Override
    public PermissionDTO getPermission(Long permissionId) {
        Permission permission = permissionDomainService.getPermission(permissionId);

        return CloneUtil.to(permission, PermissionDTO.class);
    }

    @Override
    public PermissionDTO putPermission(PermissionDTO permissionDTO) {
        Permission entity = CloneUtil.to(permissionDTO, Permission.class);
        Permission permission = permissionDomainService.putPermission(entity);

        return CloneUtil.to(permission, PermissionDTO.class);
    }

    @Override
    public Boolean editPermission(Long permissionId, PermissionDTO permissionDTO) throws BizException {
        Permission permission = CloneUtil.to(permissionDTO, Permission.class);

        Boolean isEdit = permissionDomainService.editPermission(permissionId, permission);
        if (!isEdit) {
            throw new BizException(BizCodeEnum.FAILED_OPERATION, "权限编辑失败");
        }

        return Boolean.TRUE;
    }

    @Override
    public Boolean delPermission(Long permissionId) throws BizException {
        Permission permission = new Permission();
        permission.setId(permissionId);
        permission.setDeleted(Boolean.TRUE);

        Boolean isEdit = permissionDomainService.editPermission(permissionId, permission);
        if (!isEdit) {
            throw new BizException(BizCodeEnum.FAILED_OPERATION, "权限删除失败");
        }

        return Boolean.TRUE;
    }

    @Override
    public PageDTO<RoleDTO> rolePage(RoleDTO roleDTO, PageParam pageParam) throws BizException {
        Role role = CloneUtil.to(roleDTO, Role.class);

        Page<Role> page = new Page<>(pageParam.getPage(), pageParam.getPageSize());
        LambdaQueryWrapper<Role> query = Wrappers.lambdaQuery(Role.class).eq(Role::getDeleted, Boolean.FALSE);
        page = roleDomainService.rolePage(page, query);

        return CloneUtil.toPageDTO(page, RoleDTO.class);
    }
}
